/*
 *  ImageFileJpeg.cpp
 *  Ray Tracing
 *
 *  Created by Jean-Marc Comby + Romain Dequesne on Sat May 15 2004.
 *  Copyright (c) 2004. All rights reserved.
 *
 */

#include "ImageFileJpeg.hpp"

#include "jpeglib.h"

#include "Utils/Erreur.hpp"
#include "Utils/borne.hpp"

#include <stdlib.h>

using namespace std;

/**
 * Construceur permettant d'initialiser une ImageFileJpeg à partir
 * d'une Image, un nom de fichier et la qualité voulue.
 *
 * @param im pointeur sur l'Image que l'on veut sauver.
 * @param nom nom du fichier dans lequel l'Image sera sauvée.
 * @param qualite nombre inversément proportionnel au taux de
 * compression, qui est automatiquement ramené dans l'intervalle
 * 1-100.
 */
ImageFileJpeg::ImageFileJpeg(Image *im, const string& nom, const int& qualite)
  : ImageFile( im, nom ), Qualite( borne( qualite, 1, 100 ) )
{}

/**
 * Constructeur permettant d'initialiser une ImageFileJpeg de zéro.
 *
 * @param h dimension horizontale de l'image.
 * @param l dimension verticale de l'image.
 * @param nom nom du fichier dans lequel l'Image sera sauvée.
 * @param qualite nombre inversément proportionnel au taux de
 * compression, qui est automatiquement ramené dans l'intervalle
 * 1-100.
 */
ImageFileJpeg::ImageFileJpeg(const unsigned int& h, const unsigned int& l,
			     const string& nom, const int& qualite)
  : ImageFile( h, l, nom ), Qualite( borne( qualite, 1, 100 ) )
{}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
ImageFileJpeg::~ImageFileJpeg()
{}

/**
 * Méthode ouvrant le fichier. Si l'ouverture n'est pas possible, une
 * erreur est écrite dans le log, puis on quitte la méthode.
 *
 * Une fois le fichier ouvert, on comprime l'image, puis on écrit le
 * fichier.
 */
void ImageFileJpeg::affiche()
{
  cout << "Sauvegarde de l image " << outputfile << endl;
  unsigned char **image[3];		 // tableau tampon
  struct jpeg_compress_struct cinfo;     // compression object
  struct jpeg_error_mgr jerr;            // error object
  FILE *outfile;                         // output jpeg file
  int row_stride;
	
	
  // Now try to open the output jpg file. If that fails, return.
  if ((outfile = fopen(outputfile, "wb")) == NULL)
    {
      printf("Impossible d ouvrir le fichier\n Consulter le log");
      Erreur err(true,1,"Erreur d ouverture du fichier","Classe ImageFileJpeg");
      err.ecrire("Erreur.txt");
      return;
    }
	
  // The following stuff is boilerplate code, for the most part, that is
  // taken from example.c. Every compression will contain code that looks
  // very much like this. You can vary the image width and height, as well
  // as the compression quality. Most of the rest of the code will usually
  // stay the same. Although the JPEG library is incredibly flexible. You can
  // feed it grayscale data, YCrCB data. You can also plug in different
  // destination managers.
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  jpeg_stdio_dest(&cinfo, outfile);
  cinfo.image_width = Largeur;    /* image width and height, in pixels */
  cinfo.image_height = Hauteur;
  cinfo.input_components = 3;         /* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, Qualite, TRUE /* limit to baseline-JPEG values */);
	
  // Now start the compression
  jpeg_start_compress(&cinfo, TRUE);
	
  /* image allocation */
  for (int i=0;i<cinfo.input_components;i++)
    {
      image[i] = (unsigned char **)malloc((Hauteur) * ComposantCouleur::nbrBytes());
      for (unsigned int j=0;j<Hauteur;j++)
	{
	  image[i][j] = (unsigned char *)malloc((Largeur) * ComposantCouleur::nbrBytes());
	}
    }
	
  // copie de notre image dans ce tableau tampon
  Couleur color;
  for (unsigned int pixel_y(0); pixel_y < Hauteur; pixel_y++)
    {
      for (unsigned int pixel_x(0); pixel_x < Largeur; pixel_x++)
	{
	  color=Screen[pixel_x][pixel_y];
	  image[0][pixel_y][pixel_x]=color.rouge();
	  image[1][pixel_y][pixel_x]=color.vert();
	  image[2][pixel_y][pixel_x]=color.bleu();
	}
    }

  row_stride = Largeur * cinfo.input_components;
  JSAMPARRAY buffer;
  buffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
  while (cinfo.next_scanline < cinfo.image_height)
    {
      for (unsigned int i=0; i<Largeur; i++)
	for (int n=0; n<cinfo.input_components; n++)
	  buffer[0][n+cinfo.input_components*i] = image[n][cinfo.next_scanline][i];
      (void) jpeg_write_scanlines(&cinfo, buffer, 1);
    }	
  // finish the compreession, close the file handle, and then destroy the
  // compression object.
  jpeg_finish_compress(&cinfo);
  fclose(outfile);      
  jpeg_destroy_compress(&cinfo);
  // destruction de image tampon
  for (int i=0;i<cinfo.input_components;i++)
    {
      for (unsigned int j=0;j<Hauteur;j++)
	free(image[i][j]);
      free(image[i]);
    }
}
